package es.usc.citius.servando.calendula.database.migrationHelpers;

import android.database.sqlite.SQLiteDatabase;

import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.table.TableUtils;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;

import es.usc.citius.servando.calendula.CalendulaApp;
import es.usc.citius.servando.calendula.database.DB;
import es.usc.citius.servando.calendula.drugdb.DBRegistry;
import es.usc.citius.servando.calendula.drugdb.model.persistence.ActiveIngredient;
import es.usc.citius.servando.calendula.drugdb.model.persistence.ContentUnit;
import es.usc.citius.servando.calendula.drugdb.model.persistence.Excipient;
import es.usc.citius.servando.calendula.drugdb.model.persistence.HomogeneousGroup;
import es.usc.citius.servando.calendula.drugdb.model.persistence.PackageType;
import es.usc.citius.servando.calendula.drugdb.model.persistence.Prescription;
import es.usc.citius.servando.calendula.drugdb.model.persistence.PrescriptionActiveIngredient;
import es.usc.citius.servando.calendula.drugdb.model.persistence.PrescriptionExcipient;
import es.usc.citius.servando.calendula.drugdb.model.persistence.PresentationForm;
import es.usc.citius.servando.calendula.events.PersistenceEvents;
import es.usc.citius.servando.calendula.persistence.Medicine;
import es.usc.citius.servando.calendula.util.LogUtil;
import es.usc.citius.servando.calendula.util.PreferenceKeys;
import es.usc.citius.servando.calendula.util.PreferenceUtils;
import kotlin.jvm.JvmStatic;
import kotlin.jvm.Throws;

public class DrugModelMigrationHelper {

    private static final String TAG = "DrugModelMigration";

    @Throws(exceptionClasses = SQLException.class)
    @JvmStatic
    public static void migrateDrugModel(SQLiteDatabase db, ConnectionSource connectionSource) {

        // drug model classes that are persisted to db
        List<Class<?>> drugDbClasses = Arrays.asList(
                ActiveIngredient.class,
                ContentUnit.class,
                Excipient.class,
                HomogeneousGroup.class,
                PackageType.class,
                Prescription.class,
                PresentationForm.class,
                PrescriptionActiveIngredient.class,
                PrescriptionExcipient.class);

        // drop deprecated db tables
        LogUtil.d(TAG, "Dropping deprecated tables...");
        db.execSQL("DROP TABLE IF EXISTS Prescriptions;");
        db.execSQL("DROP TABLE IF EXISTS Groups;");
        // create new db tables
        LogUtil.d(TAG, "Creating new drug model tables...");
        for (Class<?> c : drugDbClasses) {
            try {
                TableUtils.createTable(connectionSource, c);
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }

        // Prompt the user for DB reinstall/med relink if needed
        PreferenceUtils.edit().putBoolean(PreferenceKeys.DRUGDB_DB_PROMPT.key(), true).commit();
        CalendulaApp.eventBus().post(new PersistenceEvents.DatabaseUpdateEvent());
    }

    @JvmStatic
    public static void linkMedsAfterUpdate() {
//        val applicableMeds = DB.medicines().findAll().filter { it.database.isNullOrEmpty() && !it.cn.isNullOrEmpty() }
        List<Medicine> medicines = DB.medicines().findAll();
        final List<Medicine> applicableMeds = new ArrayList<>();
        for (Medicine medicine : medicines) {
            if (medicine.getDatabase().isEmpty() && !medicine.getCn().isEmpty()) {
                applicableMeds.add(medicine);
            }
        }
        final String currentDB = PreferenceUtils.getString(PreferenceKeys.DRUGDB_CURRENT_DB, null);
        if (!applicableMeds.isEmpty()) {
            LogUtil.d(TAG, "linkMedsAfterUpdate: found ${applicableMeds.size} meds that can be re-linked.");
            DB.transaction(new Callable<Object>() {
                @Override
                public Object call() throws Exception {
                    for (Medicine medicine : applicableMeds) {
                        Prescription linkablePrescription = DB.drugDB().prescriptions().findByCn(medicine.getCn());
                        if (linkablePrescription != null) {
                            medicine.setDatabase(currentDB);
                            medicine.setName(linkablePrescription.shortName());
                            medicine.setPresentation(DBRegistry.instance().current().expectedPresentation(linkablePrescription));
                            DB.medicines().saveAndFireEvent(medicine);
                            LogUtil.d(TAG, "linkMedsAfterUpdate: linked med $med to prescription $linkablePrescription");
                        }
                    }
                    return this;
                }
            });
        }
    }
}
